spb/llmindex Public
The discriminative, contamination-resistant, fully transparent LLM ranking — updated live.
TypeScript 77.9%
TeX 15.2%
Python 3.7%
SQL 1.4%
JavaScript 1.1%
Shell 0.5%
1/**2 * llmindex.io — model profile: per-domain scores, sub-metrics, run history3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * License: Proprietary — © Simon-Pierre Boucher, all rights reserved6 */7import Link from 'next/link';8import { notFound } from 'next/navigation';9import { DemoBanner, ScoreBar, ScoreValue, formatMs, formatUsd } from '@llmindex/ui';10import { ProviderLogo } from '@/components/ProviderLogo';11import { getModelDuels, getModelProfile, getModelResponses } from '@/lib/data';1213export const revalidate = 3600;14export const dynamicParams = true;1516export function generateStaticParams(): Array<{ slug: string[] }> {17 return [];18}1920export default async function ModelPage({ params }: { params: { slug: string[] } }) {21 const slug = params.slug.map(decodeURIComponent).join('/');22 const profile = await getModelProfile(slug);23 if (!profile) notFound();24 const [responses, duels] = await Promise.all([getModelResponses(slug), getModelDuels(slug)]);25 const responseDomains = Object.keys(responses).sort();2627 return (28 <div className="space-y-8">29 <div className="space-y-2">30 <Link href="/" className="text-sm text-zinc-500 hover:text-zinc-900">31 ← Leaderboard32 </Link>33 <h1 className="flex items-center gap-3 text-2xl font-bold text-zinc-900">34 <ProviderLogo provider={profile.provider} size={30} />35 {profile.name}36 </h1>37 <p className="text-sm text-zinc-500">38 {profile.slug} · {profile.provider} · context{' '}39 {profile.contextLength?.toLocaleString() ?? '—'} · in{' '}40 {formatUsd(profile.promptPricePerM)}/1M · out {formatUsd(profile.completionPricePerM)}/1M41 </p>42 </div>4344 {profile.run.kind === 'demo_seed' && <DemoBanner />}4546 {profile.global && (47 <section className="rounded-lg border border-zinc-200 p-5">48 <h2 className="mb-2 text-sm uppercase tracking-wide text-zinc-500">Global Index</h2>49 <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:gap-6">50 <span className="text-4xl font-bold tabular-nums text-emerald-600">51 {profile.global.score}52 </span>53 <div className="flex-1 space-y-1">54 <ScoreBar55 score={profile.global.score}56 scoreLow={profile.global.scoreLow}57 scoreHigh={profile.global.scoreHigh}58 />59 <p className="text-xs text-zinc-500">60 95% CI [{profile.global.scoreLow}–{profile.global.scoreHigh}] · index v61 {profile.run.indexVersion}62 </p>63 </div>64 </div>65 </section>66 )}6768 <section className="space-y-3">69 <h2 className="text-xl font-semibold text-zinc-900">Per-domain scores</h2>70 <div className="overflow-x-auto">71 <table className="w-full min-w-[56rem] text-sm">72 <thead>73 <tr className="border-b border-zinc-200 text-left text-xs uppercase tracking-wide text-zinc-500">74 <th className="py-2 pr-3">Domain</th>75 <th className="py-2 pr-3 text-right">Score (95% CI)</th>76 <th className="w-1/4 py-2 pr-3"></th>77 <th className="py-2 pr-3 text-right">Accuracy (IRT)</th>78 <th className="py-2 pr-3 text-right">Consistency</th>79 <th className="py-2 pr-3 text-right">Calibration</th>80 <th className="py-2 pr-3 text-right">Contam. Δ</th>81 <th className="py-2 pr-3 text-right">p50</th>82 <th className="py-2 pr-3 text-right">$/1k</th>83 </tr>84 </thead>85 <tbody>86 {profile.domains.map((d) => (87 <tr key={d.domain} className="border-b border-zinc-100">88 <td className="py-2 pr-3 capitalize">89 <Link90 href={`/leaderboard/${d.domain}`}91 className="hover:text-emerald-600"92 >93 {d.domain.replaceAll('_', ' ')}94 </Link>95 </td>96 <td className="py-2 pr-3 text-right">97 <ScoreValue score={d.score} scoreLow={d.scoreLow} scoreHigh={d.scoreHigh} />98 </td>99 <td className="py-2 pr-3">100 <ScoreBar score={d.score} scoreLow={d.scoreLow} scoreHigh={d.scoreHigh} />101 </td>102 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">103 {d.subMetrics?.accuracy_irt?.toFixed(3) ?? '—'}104 </td>105 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">106 {d.subMetrics?.consistency?.toFixed(2) ?? '—'}107 </td>108 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">109 {d.subMetrics?.calibration?.toFixed(2) ?? '—'}110 </td>111 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">112 {d.subMetrics?.contamination_delta?.toFixed(3) ?? '—'}113 </td>114 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">115 {formatMs(d.subMetrics?.latency_p50)}116 </td>117 <td className="py-2 pr-3 text-right tabular-nums text-zinc-600">118 {formatUsd(d.subMetrics?.cost_per_1k_items)}119 </td>120 </tr>121 ))}122 </tbody>123 </table>124 </div>125 </section>126127 {responseDomains.length > 0 && (128 <section className="space-y-3">129 <h2 className="text-xl font-semibold text-zinc-900">Every answer, every test</h2>130 <p className="text-sm text-zinc-500">131 Full transparency: the model's latest graded answer on every instantiated item of132 the current index version. Answer keys never leave the server; anchor-item prompts are133 withheld to protect the longitudinal subset.134 </p>135 {responseDomains.map((domain) => {136 const rows = responses[domain]!;137 const nCorrect = rows.filter((r) => r.correct === true).length;138 return (139 <details key={domain} className="rounded-xl border border-zinc-200">140 <summary className="cursor-pointer select-none px-4 py-3 text-sm font-medium capitalize text-zinc-800 hover:bg-zinc-50">141 {domain.replaceAll('_', ' ')}{' '}142 <span className="ml-2 text-xs tabular-nums text-zinc-500">143 {nCorrect}/{rows.length} correct144 </span>145 </summary>146 <div className="max-h-[32rem] space-y-2 overflow-y-auto border-t border-zinc-200 p-3">147 {rows.map((r, i) => (148 <div key={i} className="rounded-lg border border-zinc-100 bg-white p-3 text-xs">149 <div className="flex flex-wrap items-center gap-2">150 <span151 className={152 'rounded px-1.5 py-0.5 font-semibold ' +153 (r.correct === true154 ? 'bg-emerald-100 text-emerald-700'155 : r.correct === false156 ? 'bg-red-100 text-red-700'157 : 'bg-zinc-200 text-zinc-600')158 }159 >160 {r.correct === true ? 'correct' : r.correct === false ? 'wrong' : r.error ?? 'unscored'}161 </span>162 <span className="text-zinc-500">{r.templateId}</span>163 {r.isAnchor && <span className="rounded bg-amber-100 px-1.5 py-0.5 text-amber-700">anchor</span>}164 <span className="ml-auto tabular-nums text-zinc-500">165 conf {r.confidence != null ? Math.round(r.confidence * 100) + '%' : '—'} ·{' '}166 {formatMs(r.latencyMs)} · {formatUsd(r.costUsd)} · {r.tokensOut ?? '—'} tok167 </span>168 </div>169 {r.prompt && (170 <details className="mt-2">171 <summary className="cursor-pointer text-zinc-500">question</summary>172 <pre className="mt-1 max-h-48 overflow-auto whitespace-pre-wrap rounded bg-zinc-100 p-2 text-[11px] text-zinc-600">173 {r.prompt}174 </pre>175 </details>176 )}177 <div className="mt-2">178 <span className="text-zinc-500">model answer: </span>179 <code className="break-all text-zinc-800">{r.answer ?? '(none extracted)'}</code>180 </div>181 </div>182 ))}183 </div>184 </details>185 );186 })}187 </section>188 )}189190 {duels.length > 0 && (191 <section className="space-y-3">192 <h2 className="text-xl font-semibold text-zinc-900">Judge-rated duels</h2>193 <p className="text-sm text-zinc-500">194 Pairwise duels (writing, safety, SVG logo design) rated by a cross-provider judge195 panel with recorded position swaps.196 </p>197 <div className="space-y-2">198 {duels.map((d, i) => (199 <details key={i} className="rounded-xl border border-zinc-200">200 <summary className="cursor-pointer select-none px-4 py-3 text-sm hover:bg-zinc-50">201 <span202 className={203 'mr-2 rounded px-1.5 py-0.5 text-xs font-semibold ' +204 (d.outcome === 'win'205 ? 'bg-emerald-100 text-emerald-700'206 : d.outcome === 'loss'207 ? 'bg-red-100 text-red-700'208 : 'bg-zinc-200 text-zinc-600')209 }210 >211 {d.outcome}212 </span>213 <span className="capitalize text-zinc-700">{d.domain.replaceAll('_', ' ')}</span>214 <span className="text-zinc-500"> vs {d.opponent} · judge {d.judge}</span>215 {d.positionSwapped && <span className="ml-1 text-xs text-zinc-400">(positions swapped)</span>}216 </summary>217 <div className="space-y-2 border-t border-zinc-200 p-3 text-xs">218 <div>219 <span className="text-zinc-500">task: </span>220 <span className="text-zinc-700">{d.prompt.slice(0, 400)}</span>221 </div>222 {d.myResponseExcerpt && (223 <pre className="max-h-48 overflow-auto whitespace-pre-wrap rounded bg-zinc-100 p-2 text-[11px] text-zinc-600">224 {d.myResponseExcerpt}225 </pre>226 )}227 </div>228 </details>229 ))}230 </div>231 </section>232 )}233234 <section className="space-y-3">235 <h2 className="text-xl font-semibold text-zinc-900">Run history</h2>236 <ul className="space-y-1 text-sm text-zinc-600">237 {profile.runHistory.map((h) => (238 <li key={h.runId} className="flex gap-4">239 <span className="tabular-nums">{new Date(h.createdAt).toISOString().slice(0, 10)}</span>240 <span>v{h.indexVersion}</span>241 <span className="text-zinc-500">{h.kind}</span>242 <span className="tabular-nums text-zinc-800">{h.score ?? '—'}</span>243 </li>244 ))}245 </ul>246 </section>247 </div>248 );249}250